#ifndef PURE_STRING_H
#define PURE_STRING_H

#include "PureObject.h"
#include <stdlib.h>
#include <string.h>

namespace Pure
{
    /**
     * Allocates a string of size len and returns a pointer to
     * the allocated memory. Can be free'd with free().
     * @param src The source string to be copied.
     * @param len The length to be copied.
     */
    char* strndup(const char* src, size_t len);

    class String : public Object
    {
    public:
        String() : mData(0), mLength(0) {}
        explicit String(size_t len) : mData(0)        { this->allocData(NULL, len); }
        explicit String(const char utf8[]) : mData(0) { this->allocData(utf8); }
        explicit String(const String& str) : mData(0)
        {
            *this = str;
        }
        ~String();

        const char* utf8() const { return mData; }
        char*       utf8()       { return mData; }
        
        size_t  length() const   { return mLength; }
        
        void append(const char utf8[], size_t len);
        void append(const char utf8[]);
        void append(const String& str, size_t len) 
        {
            assert(len <= str.mLength);
            this->append(str.mData, len);
        }
        void append(const String& str)
        {
            this->append(str.mData, str.mLength);
        }
        void append(char c)
        {
            this->append(&c, 1);
        }

        void clear(bool freeData = false);
        
        bool equals(const char utf8[], size_t len) const;
        bool equals(const char utf8[]) const;
        bool equals(const String& str) const
        {
            return this->equals(str.mData, str.mLength);
        }
        bool equals(const String& str, size_t len) const
        {
            return this->equals(str.mData, len);
        }

        String& operator+=(const char utf8[])
        {
            this->append(utf8);
            return *this;
        }
        String& operator+=(const String& str)
        {
            this->append(str.mData, str.mLength);
            return *this;
        }
        String& operator+=(char c)
        {
            this->append(&c, 1);
            return *this;
        }
        String& operator=(const char utf8[])
        {
            this->allocData(utf8);
            return *this;
        }
        String& operator=(const String& str)
        {
            this->allocData(str.mData, str.mLength);
            return *this;
        }
        String& operator=(char c)
        {
            this->allocData(&c, 1);
            return *this;
        }

        char    operator[](size_t idx) const 
        {
            assert(idx < mLength);
            return mData[idx];
        }
        char&   operator[](size_t idx)
        {
            assert(idx < mLength);
            return mData[idx];
        }
        
    private:
        void allocData(const char utf8[]);
        void allocData(const char utf8[], size_t len);
        char*   mData;
        size_t  mLength;
    };
    
    inline bool operator==(const String& left, const String& right)
    {
        return left.equals(right);
    }
    
    inline bool operator==(const String& str, const char utf8[])
    {
        return str.equals(utf8);
    }
    
    inline bool operator==(const char utf8[], const String& str)
    {
        return str.equals(utf8);
    }
    
    inline bool operator!=(const String& left, const String& right)
    {
        return !left.equals(right);
    }
    
    inline bool operator!=(const String& str, const char utf8[])
    {
        return !str.equals(utf8);
    }
    
    inline bool operator!=(const char utf8[], const String& str)
    {
        return !str.equals(utf8);
    }
}

#endif
